﻿// <copyright file="Program.cs" company="Pelican Design &amp; Development">
//      Copyright (c) Pelican Design &amp; Development. All Rights Reserved.
// </copyright>

namespace AppDomains
{
    using System;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Reflection;
    using System.Security;
    using System.Security.Permissions;
    using System.Security.Policy;

    /// <summary>
    /// Main program which runs with full-trust.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// Path where application executable and related full-trust libraries are stored.
        /// </summary>
        private static readonly string applicationDirectoryPath = AppDomain.CurrentDomain.BaseDirectory;

        /// <summary>
        /// Sandbox path where plugins and UI related libraries are stored.
        /// </summary>
        private static readonly string sandboxDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sandbox");

        /// <summary>
        /// File path of common tools library.
        /// </summary>
        private static readonly string toolsFilePath = Path.Combine(applicationDirectoryPath, "Tools.dll");

        /// <summary>
        /// Main entry point.
        /// </summary>
        [STAThread]
        [SecuritySafeCritical]
        public static void Main()
        {
            ////Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

            Console.WriteLine("Loading UI plugin...");
            AppDomain sandboxAppDomain = CreateSandboxDomain();

            string mainUIPlugInFilePath = Path.Combine(sandboxDirectoryPath, "MainUI.dll");
            CommonMainUI mainUI = (CommonMainUI)sandboxAppDomain.CreateInstanceFromAndUnwrap(mainUIPlugInFilePath, "AppDomains.MainUI", true, BindingFlags.CreateInstance, null, null, null, null);

            ////mainUI.TestFilePermissions();

            Console.WriteLine("Loading plugins...");

            Collection<CommonPlugIn> plugIns = new Collection<CommonPlugIn>();

            // For sake of simplicity, we hardcode the list of plugins.
            string greenPlugInFilePath = Path.Combine(sandboxDirectoryPath, "GreenPlugIn.dll");
            CommonPlugIn greenPlugIn = (CommonPlugIn)sandboxAppDomain.CreateInstanceFromAndUnwrap(greenPlugInFilePath, "GreenPlugIn.DemoGreenPlugIn", true, BindingFlags.CreateInstance, null, null, null, null);
            plugIns.Add(greenPlugIn);

            string bluePlugInFilePath = Path.Combine(sandboxDirectoryPath, "BluePlugIn.dll");
            CommonPlugIn bluePlugIn = (CommonPlugIn)sandboxAppDomain.CreateInstanceFromAndUnwrap(bluePlugInFilePath, "BluePlugIn.DemoBluePlugIn", true, BindingFlags.CreateInstance, null, null, null, null);
            plugIns.Add(bluePlugIn);

            mainUI.UsePlugIns(plugIns);

            Console.WriteLine("Loaded. Displaying dialog...");

            mainUI.ShowDialog();

            // TODO: see http://www.infosysblogs.com/microsoft/2008/10/working_with_application_domai_1.html
            ////AppDomain.Unload(sandboxAppDomain);

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey(true);
        }

        /// <summary>
        /// Loads a new UI logic class from a plugin.
        /// </summary>
        /// <remarks>Remember to unload AppDomain after finishing using it.</remarks>
        /// <returns>UI logic class from plugin which will be executed inside sandbox.</returns>
        private static AppDomain CreateSandboxDomain()
        {
            PermissionSet mainUIplugInPermissions = new PermissionSet(PermissionState.None);
            mainUIplugInPermissions.AddPermission(new UIPermission(PermissionState.Unrestricted));

            // Set execution permission to be able to run code, and unmanaged code permission to be able to display WPF window.
            mainUIplugInPermissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution | SecurityPermissionFlag.Assertion | SecurityPermissionFlag.UnmanagedCode));

            // Set tools directory discoverable.
            mainUIplugInPermissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery, applicationDirectoryPath));

            // Set read permissions to DLLs, that is to say to all plugins directory and to the DLL containing the tools.
            mainUIplugInPermissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, sandboxDirectoryPath));
            mainUIplugInPermissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, toolsFilePath));

            // Ensures that common tools assembly has enough privileges.
            Assembly commonAssembly = typeof(DataTools).Assembly;
            StrongName commonStrongName = commonAssembly.Evidence.GetHostEvidence<StrongName>();

            AppDomainSetup appDomainSetup = AppDomain.CurrentDomain.SetupInformation;
            AppDomain sandboxAppDomain = AppDomain.CreateDomain("UISandbox", null, appDomainSetup, mainUIplugInPermissions, new[] { commonStrongName });
            return sandboxAppDomain;
        }
    }
}
